package cn._2dland.sakushi;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.Checksum;

import javax.servlet.http.HttpServletResponse;

import cn._2dland.http.HttpClient;
import cn._2dland.http.method.PostMethod;
import cn._2dland.sakushi.util.JISFace;

public class Sakushi {
	private static Sakushi instance = new Sakushi();

	private Checksum crc32 = null;

	public Sakushi() {
		crc32 = new CRC32();
	}

	public static Sakushi getInstance() {
		return instance;
	}

	/**
	 * 受理请求
	 * 请求的格式为：“课题?选项1|选项2|选项3...”或“课题？选项1；选项2...”
	 * @param customer - 请求者
	 * @param request - 请求串
	 */
	public Policy accept(String customer, String request) throws InvalidRequestException {
		Policy p = null;
		// 解析请求
		Request req = Request.parse(request);
		// 如果解析失败，则抛出异常
		if(req.isEmpty())
			throw new InvalidRequestException(request);

		// 对选项进行决策
		List<Option> options = analyze(customer, req);

		// 返回决策结果
		String content = getResultText(req.getProblem(), options);
		byte[] chart = req.isNeedChart() ? getResultChart(req.getProblem(), options) : null;
		p = new Policy(content, chart);
		return p;
	}

	/**
	 * 获取帮助信息
	 * 暂不开放
	 * @return
	 */
	public String help() {
		StringBuffer buf = new StringBuffer();
		buf.append("用法说明：").append("发送消息");
		return buf.toString();
	}

	/***
	 * 分析各个选项的权重，并按从高到低的顺序排序
	 * @param customer 请求者
	 * @param request 请求内容
	 * @return 排序后的选项
	 */
	private List<Option> analyze(String customer, Request request) {
		// 问题和选项列表
		String problem = request.getProblem();
		Collection<String> choices = request.getChoices();

		// 初始化结果列表
		List<Option> opts = new ArrayList<Option>(choices.size());

		// 计算课题权重
		long problemWeight = getStringWeight(problem);

		// 计算选项权重，并排序
		for(String choice : choices) {
			// 计算权重
			String maskedChoice = customer + ":" + replace(problem + ":" + choice);
			double weight = (double)(getStringWeight(maskedChoice)) / problemWeight;
			// 构造选项
			Option opt = new Option(choice, weight);

			// 进行插入排序
			boolean hasInserted = false;
			for(int j=0; j<opts.size(); j++) {
				Option opt2 = opts.get(j);
				if(opt2.getWeight() < opt.getWeight()) {
					opts.add(j, opt);
					hasInserted = true;
					break;
				}
			}
			if(!hasInserted) opts.add(opt);
		}
		return opts;
	}

	/** 时间日期替换：将字符串中表示时间、日期的单词替换为对应的数值 */
	private String replace(String str) {
		String result = str;
		// 获取当前时间
		Calendar cal = Calendar.getInstance();
		long time = System.currentTimeMillis();
		SimpleDateFormat fmt = new SimpleDateFormat();

		// 替换年
		fmt.applyPattern("yyyy年");
		String year = fmt.format(cal.getTime());
		result = result.replaceAll("今年", year);

		cal.add(Calendar.YEAR, -1);
		String lastYear = fmt.format(cal.getTime());
		result = result.replaceAll("去年", lastYear);

		cal.add(Calendar.YEAR, 2);
		String nextYear = fmt.format(cal.getTime());
		result = result.replaceAll("明年", nextYear);

		// 替换月
		fmt.applyPattern("yyyy年MM月");
		cal.setTimeInMillis(time);
		String month = fmt.format(cal.getTime());
		result = result.replaceAll("本月", month);

		cal.add(Calendar.MONTH, -1);
		String lastMonth = fmt.format(cal.getTime());
		result = result.replaceAll("上月", lastMonth);

		cal.add(Calendar.MONTH, 2);
		String nextMonth = fmt.format(cal.getTime());
		result = result.replaceAll("下月", nextMonth);

		// 替换日
		fmt.applyPattern("yyyy年MM月dd日");
		cal.setTimeInMillis(time);
		String today = fmt.format(cal.getTime());
		result = result.replaceAll("今天", today);

		cal.add(Calendar.DATE, -1);
		String yesterday = fmt.format(cal.getTime());
		result = result.replaceAll("昨天", yesterday);

		cal.add(Calendar.DATE, 2);
		String tomorrow = fmt.format(cal.getTime());
		result = result.replaceAll("明天", tomorrow);

		// 替换时间
		fmt.applyPattern("yyyy年MM月dd日 hh时mm分ss秒");
		cal.setTimeInMillis(time);
		String now = fmt.format(cal.getTime());
		result = result.replaceAll("现在", now);

		// 替换其他标记
		fmt.applyPattern("yyyy年MM月dd日18时");
		cal.setTimeInMillis(time);
		String tonight = fmt.format(cal.getTime());
		result = result.replace("今晚", tonight);

		return result;
	}

	/** 计算文本权重 */
	private synchronized long getStringWeight(String str) {
		long weight = 1;
		try{
			byte[] b = str.getBytes("utf-8");
			crc32.update(b, 0, b.length);
			weight = crc32.getValue();
			crc32.reset();
		}
		catch (Exception e) {
			weight = 1;
		}
		return weight;
	}

	/** 获取结果文本 */
	private String getResultText(String problem, List<Option> options) {
		StringBuffer buf = new StringBuffer();
		buf.append("「").append(problem).append("」结果发表").append(JISFace.pickFace()).append(" ");
		int size = options.size();

		// 统计总加权
		double sum = 0;
		for(Option opt : options) {
			sum += opt.getWeight();
		}
		if(sum == 0) sum = 1;

		// 计算比例
		double psum = 0;
		for(int i=0; i<size; i++) {
			Option opt = options.get(i);
			double percent = 0;
			if(i != (size - 1)){
				// 计算百分比
				percent = (double)Math.round(opt.getWeight() / sum * 10000) / 100;
				psum += percent;
			}
			else {
				// 最后一项的百分比通过减法计算
				percent = (double)Math.round((100 - psum) * 100) / 100;
			}
			buf.append(opt.getLabel()).append("(").append(percent).append("%)；");
		}
		return buf.toString();
	}

	/** Google Chart API */
	private static final String GOOGLE_CHART_API = "http://chart.googleapis.com/chart";
	/** 图表宽度 */
	private static final int CHART_WIDTH = 360;
	/** 图表高度 */
	private static final int CHART_HEIGHT = 240;
	/** 系列调色盘 */
	private static final String[] PALETTE = {"FED94E", "6887B5", "D2695E", "72AF6E", "8B6EB0", "F0AB43"};

	/** 调用GoogleAPI，生成图表 */
	private synchronized byte[] getResultChart(String title, List<Option> series) {
		byte[] buf = null;
		try {
			HttpClient client = new HttpClient();
			client.getClientParams().setClientCharset("utf-8");
			PostMethod post = new PostMethod(GOOGLE_CHART_API);

			// 设置统计图参数
			post.addParameter("chtt", title);	// 图表标题
			post.addParameter("chts", "FFFFFF,24");	// 图表标题样式
			post.addParameter("cht", "p");		// 图表类型
			post.addParameter("chs", CHART_WIDTH + "x" + CHART_HEIGHT);	// 图表大小
			post.addParameter("chf", "bg,lg,90,000000,1,4A465A,0");	// 背景填充方式
			post.addParameter("chma", "6,6,6,6");	// 内空白补丁
			post.addParameter("chxt", "x");	// 控制标签
			post.addParameter("chxs", "0,FFFFFF,14");	// 标签样式
			// 填充数据
			// 计算需要使用的颜色数
			int modder = PALETTE.length;
			while(series.size() % modder == 1){
				modder -= 1;
			}
			StringBuffer chd = new StringBuffer("t:");
			StringBuffer chl = new StringBuffer();
			StringBuffer chco = new StringBuffer();
			for(int i=0; i<series.size(); i++) {
				Option opt = series.get(i);
				chd.append(opt.getWeight()).append(",");
				chl.append(opt.getLabel()).append("|");
				chco.append(PALETTE[i % modder]).append(",");
			}
			chd.deleteCharAt(chd.length() - 1);
			post.addParameter("chd", chd.toString());	// 值列表
			chl.deleteCharAt(chl.length() - 1);
			post.addParameter("chl", chl.toString());	// 标签列表
			chco.deleteCharAt(chco.length() - 1);
			post.addParameter("chco", chco.toString());	// 调色板

			// 发送请求
			int respCode = client.executeMethod(post);
			if(respCode != HttpServletResponse.SC_OK)
				throw new Exception();

			// 读取图表
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			InputStream is = post.getResponseStream();
			for(int b=0; (b=is.read())>=0; ) {
				baos.write(b);
			}
			baos.flush();
			buf = baos.toByteArray();

			// 释放资源
			baos.close();
		}
		catch (Exception e) {
			buf = null;
			e.printStackTrace();
		}
		return buf;
	}
}
